//数据劫持的入口  value:data数据  vm:vm实例对象
function observe(value, vm) {
    if (!value || typeof value !== 'object') {
        return;
    }
    //vaule必须是对象
    return new Observer(value);
};


function Observer(data) {
    //this:Observer的实例对象
    this.data = data;
    this.walk(data);
}

Observer.prototype = {
    walk: function(data) {
        //this劫持
        var me = this;
        //拿到data所有可枚举属性的属性名组成的数组
        Object.keys(data).forEach(function(key) {
            //key:属性名  data[key]:属性名对应的属性值
            me.convert(key, data[key]);
        });
    },

    //key:属性名  val:属性名对应的属性值
    //data中有多少个属性 当前convert方法就会被调用多少次
    convert: function(key, val) {
        //this:Observer的实例对象
        //this.data:完整的data数据
        this.defineReactive(this.data, key, val);
    },


    defineReactive: function(data, key, val) {
        //分配dep  data中的每一个属性都会分配到一个dep(id:0 自增)
        var dep = new Dep();

        //递归!! 当前这个递归结束的条件:val为空 或者 不是一个对象
        var childObj = observe(val);

        //真正做数据劫持的代码!!!!
        //data: key所处的对象!
        //将每一个data中的属性重新定义!
        Object.defineProperty(data, key, {
            enumerable: true, // 可枚举
            configurable: false, // 不能再define
            get: function() {
                if (Dep.target) { //watcher
                    dep.depend();
                }
                return val;
            },
            set: function(newVal) {
                //当data中的数据更新 界面要得到重新渲染
                if (newVal === val) {
                    return;
                }
                val = newVal;
                childObj = observe(newVal);
                //dep唤醒方法!!!
                dep.notify();
            }
        });
    }
};



var uid = 0;

function Dep() {
    this.id = uid++;
    //subs: 与这个dep有关的watcher都会存在这个subs数组中
    this.subs = [];
}

Dep.prototype = {
    addSub: function(sub) {
        //sub:watcher
        this.subs.push(sub);
    },

    depend: function() {
        //this: 一个个dep
        Dep.target.addDep(this);
    },

    removeSub: function(sub) {
        var index = this.subs.indexOf(sub);
        if (index != -1) {
            this.subs.splice(index, 1);
        }
    },

    notify: function() {
        this.subs.forEach(function(sub) {
            sub.update();
        });
    }
};

Dep.target = null;